package optimizer;

import midcode.MidCode;
import midcode.OPType;

import java.util.ArrayList;

public class Optimizer {
    public static boolean isOptimise = true;
    private final ArrayList<MidCode> midCodes;
    private final ArrayList<FuncBlock> funcBlocks;

    public Optimizer(ArrayList<MidCode> midCodes) {
        this.midCodes = midCodes;
        funcBlocks = new ArrayList<>();
    }

    public void optimize() {
        setEntry();
        genBlock();
        setRelation();
    }

    public void setEntry() {
        for (int i = 0; i < midCodes.size(); i++) {
            MidCode midCode = midCodes.get(i);
            if (midCode.getOpType() == OPType.LABEL_GEN ) {
                if (i == 0 || midCodes.get(i - 1).getOpType() != OPType.LABEL_GEN) {
                    midCode.setEntry(true);
                }
            }
            if (OPType.isEntryType(midCodes.get(i - 1).getOpType())) {
                midCode.setEntry(true);
            }
        }
    }

    public void genBlock() {
        FuncBlock curFuncBlock = null;
        BasicBlock curBasicBlock = null;
        for (int i = 0; i < midCodes.size(); i++) {
            MidCode midCode = midCodes.get(i);
            if (midCode.getOpType() == OPType.FUNC_DECL) {
                curFuncBlock = new FuncBlock(midCode);
                funcBlocks.add(curFuncBlock);
                if (i < midCodes.size() - 1 && midCodes.get(i + 1).getOpType() != OPType.FUNC_DECL) {
                    midCodes.get(i + 1).setEntry(true);
                }
            } else if (midCode.isEntry()) {
                assert curFuncBlock != null;
                curBasicBlock = new BasicBlock(curFuncBlock);
                curFuncBlock.addBasicBlock(curBasicBlock);
                if (midCode.getOpType() != OPType.LABEL_GEN) {
                    curBasicBlock.addMidCode(midCode);
                } else {
                    curBasicBlock.addLabel(midCode.getDst());
                    curFuncBlock.addLabel(midCode.getDst(), curBasicBlock);
                }
            } else if (midCode.getOpType() == OPType.LABEL_GEN) {
                assert curBasicBlock != null;
                curBasicBlock.addLabel(midCode.getDst());
                curFuncBlock.addLabel(midCode.getDst(), curBasicBlock);
            } else {
                assert curBasicBlock != null;
                curBasicBlock.addMidCode(midCode);
            }
        }
    }

    public void setRelation() {
        for (FuncBlock funcBlock : funcBlocks) {
            funcBlock.setRelation();
        }
    }

//    public void setDefUse() {
//        for (FuncBlock funcBlock : funcBlocks) {
//            funcBlock.setDefUse();
//        }
//    }
//
//    public void valSpread() {
//        for (FuncBlock funcBlock : funcBlocks) {
//            funcBlock.valSpread();
//        }
//    }
}

